package net.oschina.simpleplus.redis;

import java.net.URI;
import java.util.List;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Protocol;

public class JRedisImpl implements JRedis{
	
	private JedisPool jedisPool;
	private String host;
	private int port;
	
	private static final Logger logger = LoggerFactory.getLogger("JRedisImpl");
	
	public JRedisImpl() {
	    this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host) {
	    this(poolConfig, host, Protocol.DEFAULT_PORT, Protocol.DEFAULT_TIMEOUT, null,
	        Protocol.DEFAULT_DATABASE, null);
	  }

	  public JRedisImpl(String host, int port) {
	    this(new GenericObjectPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, null,
	        Protocol.DEFAULT_DATABASE, null);
	  }

	  public JRedisImpl(final String host) {
	    jedisPool = new JedisPool(host);
	  }

	  public JRedisImpl(final String host, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
	    jedisPool = new JedisPool(host, sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final URI uri) {
	    this(new GenericObjectPoolConfig(), uri, Protocol.DEFAULT_TIMEOUT);
	  }

	  public JRedisImpl(final URI uri, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
	    this(new GenericObjectPoolConfig(), uri, Protocol.DEFAULT_TIMEOUT, sslSocketFactory,
	        sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final URI uri, final int timeout) {
	    this(new GenericObjectPoolConfig(), uri, timeout);
	  }

	  public JRedisImpl(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
	    this(new GenericObjectPoolConfig(), uri, timeout, sslSocketFactory, sslParameters,
	        hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password) {
	    this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, null);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final boolean ssl) {
	    this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, null, ssl);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final boolean ssl,
	      final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
	      final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, null, ssl,
	        sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, final int port) {
	    this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE, null);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, final int port,
	      final boolean ssl) {
	    this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE, null,
	        ssl);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, final int port,
	      final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
	      final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE, null,
	        ssl, sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, final int port,
	      final int timeout) {
	    this(poolConfig, host, port, timeout, null, Protocol.DEFAULT_DATABASE, null);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, final int port,
	      final int timeout, final boolean ssl) {
	    this(poolConfig, host, port, timeout, null, Protocol.DEFAULT_DATABASE, null, ssl);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, final int port,
	      final int timeout, final boolean ssl, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, host, port, timeout, null, Protocol.DEFAULT_DATABASE, null, ssl,
	        sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final int database) {
	    this(poolConfig, host, port, timeout, password, database, null);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final int database, final boolean ssl) {
	    this(poolConfig, host, port, timeout, password, database, null, ssl);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final int database, final boolean ssl,
	      final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
	      final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, host, port, timeout, password, database, null, ssl, sslSocketFactory,
	        sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final int database, final String clientName) {
	    this(poolConfig, host, port, timeout, timeout, password, database, clientName, false,
	        null, null, null);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final int database, final String clientName,
	      final boolean ssl) {
	    this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl,
	        null, null, null);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      int timeout, final String password, final int database, final String clientName,
	      final boolean ssl, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl,
	        sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final String host, int port,
	      final int connectionTimeout, final int soTimeout, final String password, final int database,
	      final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
	    jedisPool = new JedisPool(poolConfig, host, port, connectionTimeout, soTimeout, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
	    this.host = host;
		this.port = port;
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final URI uri) {
	    this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final URI uri,
	      final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
	      final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT, sslSocketFactory, sslParameters,
	        hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout) {
	    this(poolConfig, uri, timeout, timeout);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout,
	      final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters,
	      final HostnameVerifier hostnameVerifier) {
	    this(poolConfig, uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final URI uri,
	      final int connectionTimeout, final int soTimeout) {
		  jedisPool = new JedisPool(poolConfig, uri, connectionTimeout, soTimeout);
	  }

	  public JRedisImpl(final GenericObjectPoolConfig poolConfig, final URI uri,
	      final int connectionTimeout, final int soTimeout, final SSLSocketFactory sslSocketFactory,
	      final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) {
		  jedisPool = new JedisPool(poolConfig, uri, connectionTimeout, soTimeout, sslSocketFactory, sslParameters, hostnameVerifier);
	  }

	private Jedis getJedis(){
		return jedisPool.getResource();
	}
	
	
	private void close(Jedis jedis){
		jedis.close();
	}
	
	@Override
	public byte[] get(byte[] key){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			return jedis.get(key);
		} catch (Exception e) {
			logger.error("when execue JRedis.get() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}

	@Override
	public void set(byte[] key, byte[] value){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			jedis.set(key, value);
		} catch (Exception e) {
			logger.error("when execue JRedis.set() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}
	
	@Override
	public boolean setnx(byte[] key, byte[] value){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			return 1 == jedis.setnx(key,value);
		} catch (Exception e) {
			logger.error("when execue JRedis.setnx() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}
	
	
	@Override
	public void setex(byte[] key,int seconds, byte[] value){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			jedis.setex(key, seconds, value);
		} catch (Exception e) {
			logger.error("when execue JRedis.setex() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}
	
	@Override
	public void psetex(byte[] key,long millSeconds, byte[] value){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			jedis.psetex(key, millSeconds, value);
		} catch (Exception e) {
			logger.error("when execue JRedis.psetex() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}
	
	@Override
	public void del(byte[] key){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			jedis.del(key);
		} catch (Exception e) {
			logger.error("when execue JRedis.psetex() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}

	@Override
	public void del(byte[]... keys){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			jedis.del(keys);
		} catch (Exception e) {
			logger.error("when execue JRedis.psetex() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}
	
	@Override
	public List<byte[]> mget(final byte[]... key){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			return jedis.mget(key);
		} catch (Exception e) {
			logger.error("when execue JRedis.mget() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}

	@Override
	public long incr(byte[] key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			return jedis.incr(key);
		} catch (Exception e) {
			logger.error("when execue JRedis.incr() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}

	@Override
	public long decr(byte[] key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			return jedis.decr(key);
		} catch (Exception e) {
			logger.error("when execue JRedis.decr() host:{}, port:{} error", host, port, e);
			throw new RuntimeException(e);
		}finally {
			if(jedis != null){
				close(jedis);
			}
		}
	}

//	@Override
//	public void mset(byte[]... keyvalues){
//		Jedis jedis = null;
//		try {
//			jedis = getJedis();
//			jedis.mset(keyvalues);
//		} catch (Exception e) {
//			logger.error("when execue JRedis.mset() host:{}, port:{} error", host, port, e);
//			throw new RuntimeException(e);
//		}finally {
//			if(jedis != null){
//				close(jedis);
//			}
//		}
//	}
}
